#include "stdafx.h"

#include <serialization/addrinfo.h>
#include <map>

extern WSASetLastError_proxy_t pTrueWSASetLastError;

std::map<PADDRINFOA, u_int> allocatedProxyAddInfoAs;
std::map<PADDRINFOW, u_int> allocatedProxyAddInfoWs;

int getaddrinfo_proxy (
    __in_opt  PCSTR             pNodeName,
    __in_opt  PCSTR             pServiceName,
    __in_opt  const ADDRINFOA*  pHints,
    __out     PADDRINFOA*       ppResult
    )
{
    try
    {
    //SEND
        Header header(GetCurrentThreadId(), "getaddrinfo");
        getaddrinfo_in_inout getaddrinfoIN(pNodeName, pServiceName, 
            const_cast<ADDRINFOA*>(pHints));
        
        std::vector<char> out_buffer;
        Serialize(header, getaddrinfoIN, out_buffer);

        SendToClient (out_buffer);
        
    //RECEIVE()
        getaddrinfo_out_inout getaddrinfoOUT;

        std::vector<char> in_buffer;
        
        RecvFromClient (in_buffer, header);
        Deserialize deserialize(in_buffer);
        deserialize(getaddrinfoOUT);

        if(getaddrinfoOUT.wsa_last_error_ != 0)
        {
            pTrueWSASetLastError(getaddrinfoOUT.wsa_last_error_);            
        }

        CreateAddrinfoFromStdList (getaddrinfoOUT.ppResult_val_, ppResult);
        allocatedProxyAddInfoAs[*ppResult] = getaddrinfoOUT.ppResult_ptr_;

        return getaddrinfoOUT.ret_;
    }
    catch (std::exception& ex)
    {
        LOG(plog::error) << "getaddrinfo: " << ex.what();
    }
    pTrueWSASetLastError (WSATRY_AGAIN);
    return WSATRY_AGAIN;
}

int GetAddrInfoW_proxy (
    __in_opt  PCWSTR             pNodeName,
    __in_opt  PCWSTR             pServiceName,
    __in_opt  const ADDRINFOW*  pHints,
    __out     PADDRINFOW*       ppResult
    )
{
    try
    {
    //SEND
        Header header(GetCurrentThreadId(), "GetAddrInfoW");
        GetAddrInfoW_in_inout 
            GetAddrInfoWIN(pNodeName, pServiceName, const_cast<ADDRINFOW*>(pHints));
        
        std::vector<char> out_buffer;
        Serialize(header, GetAddrInfoWIN, out_buffer);

        SendToClient (out_buffer);
        
    //RECEIVE()
        GetAddrInfoW_out_inout GetAddrInfoWOUT;

        std::vector<char> in_buffer;
        
        RecvFromClient (in_buffer, header);
        Deserialize deserialize(in_buffer);
        deserialize(GetAddrInfoWOUT);

        if(GetAddrInfoWOUT.wsa_last_error_ != 0)
        {
            pTrueWSASetLastError(GetAddrInfoWOUT.wsa_last_error_);            
        }

        CreateAddrinfoFromStdList (GetAddrInfoWOUT.ppResult_val_, ppResult);
        allocatedProxyAddInfoWs[*ppResult] = GetAddrInfoWOUT.ppResult_ptr_;

        return GetAddrInfoWOUT.ret_;
    }
    catch (std::exception& ex)
    {
        LOG(plog::error) << "GetAddrInfoW: " << ex.what();
    }
    pTrueWSASetLastError (WSATRY_AGAIN);
    return WSATRY_AGAIN;
}

void freeaddrinfo_proxy (__in ADDRINFOA* ai)
{
    try
    {
    //SEND
        Header header(GetCurrentThreadId(), "freeaddrinfo");
        freeaddrinfo_in_inout freeaddrinfoIN( allocatedProxyAddInfoAs[ai] );
        
        std::vector<char> out_buffer;
        Serialize(header, freeaddrinfoIN, out_buffer);

        SendToClient (out_buffer);
        
    //RECEIVE()
        freeaddrinfo_out_inout freeaddrinfoOUT;

        std::vector<char> in_buffer;
        
        RecvFromClient (in_buffer, header);
        //Deserialize deserialize(in_buffer);
        //deserialize(freeaddrinfoOUT);
        allocatedProxyAddInfoAs.erase (ai);
        DestroyAddrinfoFromStdList (ai);
    }
    catch (std::exception& ex)
    {
        LOG(plog::error) << "freeaddrinfo: " << ex.what();
    }
}

void FreeAddrInfoW_proxy (__in ADDRINFOW* ai)
{
    try
    {
    //SEND
        Header header(GetCurrentThreadId(), "FreeAddrInfoW");
        FreeAddrInfoW_in_inout FreeAddrInfoWIN( allocatedProxyAddInfoWs[ai] );
        
        std::vector<char> out_buffer;
        Serialize(header, FreeAddrInfoWIN, out_buffer);

        SendToClient (out_buffer);
        
    //RECEIVE()
        FreeAddrInfoW_out_inout FreeAddrInfoWOUT;

        std::vector<char> in_buffer;
        
        RecvFromClient (in_buffer, header);
        //Deserialize deserialize(in_buffer);
        //deserialize(FreeAddrInfoWOUT);
        allocatedProxyAddInfoWs.erase (ai);
        DestroyAddrinfoFromStdList (ai);
    }
    catch (std::exception& ex)
    {
        LOG(plog::error) << "FreeAddrInfoW: " << ex.what();
    }
}

////////////////////////////////////////////////////////////
void ConvertToAddrinfoStdList (
    __in  const ADDRINFOA*                ai,
    __out std::list<ADDRINFOA_STD_LIST>&  ai_list
    )
{
    const ADDRINFOA*    ai_ptr = ai;
    ADDRINFOA_STD_LIST  current;
    
    while (ai_ptr)
    {
        current.ai_flags = ai_ptr->ai_flags;
        current.ai_family = ai_ptr->ai_family;
        current.ai_socktype = ai_ptr->ai_socktype;
        current.ai_protocol = ai_ptr->ai_protocol;
        current.ai_addrlen = ai_ptr->ai_addrlen;
        current.ai_canonname = 
            (ai_ptr->ai_canonname == NULL) ? "" : ai_ptr->ai_canonname;
        
        if (ai_ptr->ai_addr)
        {
            current.ai_addr_val.sa_family = ai_ptr->ai_addr->sa_family;
            strncpy_s (current.ai_addr_val.sa_data, 14, ai_ptr->ai_addr->sa_data, 14);
        } else {
            current.ai_addr_val.sa_family = -1;
        }

        ai_list.push_back(current);
        ai_ptr = ai_ptr->ai_next;
    }
}

////////////////////////////////////////////////////////////
void ConvertToAddrinfoStdList (
    __in  const ADDRINFOW*                ai,
    __out std::list<ADDRINFOW_STD_LIST>&  ai_list
    )
{
    const ADDRINFOW*    ai_ptr = ai;
    ADDRINFOW_STD_LIST  current;
    
    while (ai_ptr)
    {
        current.ai_flags = ai_ptr->ai_flags;
        current.ai_family = ai_ptr->ai_family;
        current.ai_socktype = ai_ptr->ai_socktype;
        current.ai_protocol = ai_ptr->ai_protocol;
        current.ai_addrlen = ai_ptr->ai_addrlen;
        current.ai_canonname = 
            (ai_ptr->ai_canonname == NULL) ? L"" : ai_ptr->ai_canonname;
        
        if (ai_ptr->ai_addr)
        {
            current.ai_addr_val.sa_family = ai_ptr->ai_addr->sa_family;
            strncpy_s (current.ai_addr_val.sa_data, 14, ai_ptr->ai_addr->sa_data, 14);
        } else {
            current.ai_addr_val.sa_family = -1;
        }

        ai_list.push_back(current);
        ai_ptr = ai_ptr->ai_next;
    }
}

////////////////////////////////////////////////////////////
void CreateAddrinfoFromStdList ( 
    __in  std::list<ADDRINFOA_STD_LIST>  ai_list,
    __out ADDRINFOA**                    ai
    )
{
    if (ai_list.size() == 0) {
        *ai = NULL;
        return;
    }

    *ai = new ADDRINFOA;
    ADDRINFOA*          ai_ptr = *ai;
    ADDRINFOA_STD_LIST* current;

    do
    {
        current = &ai_list.front();
        
        ai_ptr->ai_flags = current->ai_flags;
        ai_ptr->ai_family = current->ai_family;
        ai_ptr->ai_socktype = current->ai_socktype;
        ai_ptr->ai_protocol = current->ai_protocol;
        ai_ptr->ai_addrlen = current->ai_addrlen;
        if (current->ai_canonname == "")
        {
            ai_ptr->ai_canonname = NULL;
        } else {
            ai_ptr->ai_canonname = new char[current->ai_canonname.length()+1];
            strcpy_s (ai_ptr->ai_canonname, current->ai_canonname.length()+1,
                current->ai_canonname.c_str());
        }
        short tmp_cmp = current->ai_addr_val.sa_family;
        if (tmp_cmp != -1)
        {
            ai_ptr->ai_addr = new SOCKADDR;
            ai_ptr->ai_addr->sa_family = current->ai_addr_val.sa_family;
            strncpy_s (ai_ptr->ai_addr->sa_data, 14, current->ai_addr_val.sa_data, 14);
        } else {
            ai_ptr->ai_addr = NULL;
        }
        if (ai_list.size() != 1) {
            ai_ptr->ai_next = new ADDRINFOA;
        } else {
            ai_ptr->ai_next = NULL;
        }

        ai_ptr = ai_ptr->ai_next;
        ai_list.pop_front();        
    } while (!ai_list.empty());
}

////////////////////////////////////////////////////////////
void CreateAddrinfoFromStdList ( 
    __in  std::list<ADDRINFOW_STD_LIST>  ai_list,
    __out ADDRINFOW**                    ai
    )
{
    if (ai_list.size() == 0) {
        *ai = NULL;
        return;
    }

    *ai = new ADDRINFOW;
    ADDRINFOW*          ai_ptr = *ai;
    ADDRINFOW_STD_LIST* current;

    do
    {
        current = &ai_list.front();
        
        ai_ptr->ai_flags = current->ai_flags;
        ai_ptr->ai_family = current->ai_family;
        ai_ptr->ai_socktype = current->ai_socktype;
        ai_ptr->ai_protocol = current->ai_protocol;
        ai_ptr->ai_addrlen = current->ai_addrlen;
        if (current->ai_canonname == L"")
        {
            ai_ptr->ai_canonname = NULL;
        } else {
            ai_ptr->ai_canonname = new wchar_t[current->ai_canonname.length()+1];
            wcscpy_s (ai_ptr->ai_canonname, current->ai_canonname.length()+1,
                current->ai_canonname.c_str());
        }
        short tmp_cmp = current->ai_addr_val.sa_family;
        if (tmp_cmp != -1)
        {
            ai_ptr->ai_addr = new SOCKADDR;
            ai_ptr->ai_addr->sa_family = current->ai_addr_val.sa_family;
            strncpy_s (ai_ptr->ai_addr->sa_data, 14, current->ai_addr_val.sa_data, 14);
        } else {
            ai_ptr->ai_addr = NULL;
        }
        if (ai_list.size() != 1) {
            ai_ptr->ai_next = new ADDRINFOW;
        } else {
            ai_ptr->ai_next = NULL;
        }

        ai_ptr = ai_ptr->ai_next;
        ai_list.pop_front();        
    } while (!ai_list.empty());
}

////////////////////////////////////////////////////////////
void DestroyAddrinfoFromStdList ( 
    __in ADDRINFOA*  ai
    )
{
    ADDRINFOA*  ai_ptr = ai;

    if (ai == NULL) {
        return;
    }
    
    if (ai_ptr->ai_canonname != NULL)
    {
        delete[] ai_ptr->ai_canonname;
    }
    if (ai_ptr->ai_addr != NULL)
    {
        delete ai_ptr->ai_addr;
    }
    ADDRINFOA* tmp = ai_ptr;
    ai_ptr = ai_ptr->ai_next;
    delete tmp;
    
    while (ai_ptr)
    {
        if (ai_ptr->ai_canonname != NULL)
        {
            delete[] ai_ptr->ai_canonname;
        }
        if (ai_ptr->ai_addr != NULL)
        {
            delete ai_ptr->ai_addr;
        }

        ADDRINFOA* tmp = ai_ptr;
        ai_ptr = ai_ptr->ai_next;
        delete tmp; 
    }
    ai = NULL;
}

////////////////////////////////////////////////////////////
void DestroyAddrinfoFromStdList ( 
    __in ADDRINFOW*  ai
    )
{
    ADDRINFOW*  ai_ptr = ai;

    if (ai == NULL) {
        return;
    }
    
    if (ai_ptr->ai_canonname != NULL)
    {
        delete[] ai_ptr->ai_canonname;
    }
    if (ai_ptr->ai_addr != NULL)
    {
        delete ai_ptr->ai_addr;
    }
    ADDRINFOW* tmp = ai_ptr;
    ai_ptr = ai_ptr->ai_next;
    delete tmp;
    
    while (ai_ptr)
    {
        if (ai_ptr->ai_canonname != NULL)
        {
            delete[] ai_ptr->ai_canonname;
        }
        if (ai_ptr->ai_addr != NULL)
        {
            delete ai_ptr->ai_addr;
        }

        ADDRINFOW* tmp = ai_ptr;
        ai_ptr = ai_ptr->ai_next;
        delete tmp; 
    }
    ai = NULL;
}
